let logger = require("pomelo-logger").getLogger(__filename);
let DateUtil = require("../../../utils/dateUtil");
let Poker = require("../../../domain/poker/niuniu");
let Random = require("../../../utils/random");
let BaseTable = require("../base/yuanbaoTable");
let util = require("util");
let pomelo = require("pomelo");

// const ACTION_INTERVAL = 15;

const NiuTypeMultipliers = [
    [],
    [1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 5, 6, 8],
    [1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 4, 5, 6, 8]
];
const dealerDes = ["牛牛上庄", "自由抢庄", "轮庄", "固定庄", "通比牛牛"];
const multiplierTypes = ["牛七、牛八X2、牛九X3、牛牛X4", "牛七、牛八、牛九X2、牛牛X3"];

let NiuNiuYuanBaoTable = function () {
    BaseTable.call(this);
    this.dealerType = 1; //1. 牛牛上庄 2. 自由抢庄 5. 通比牛牛
    this.multiplierType = 1; //1. 牛七 牛八 *2
    this.maxAuctionDealerMultiplier = 1;  //最大抢庄1, 2, 3
    this.dealerUid = 0;
    this.step = 0;  //1 等待抢庄, 2 等待下注, 3 等待组牌提交
    this.stepTS = 0;
    this.lastGameDealerCandiateIds = [];
    this.actionInterval = 15; // 根据实际人数调整
};

util.inherits(NiuNiuYuanBaoTable, BaseTable);

NiuNiuYuanBaoTable.prototype.start = function () {
    BaseTable.prototype.start.call(this);
    let playingPlayers = this.getPlayingPlayers();
    let hands = Poker.dealCards(playingPlayers.length);
    let fee = this.getFee();
    for (let i = 0; i < playingPlayers.length; i++) {
        let player = playingPlayers[i];
        player.start(hands[i], fee);
    }
    this.broadCast("onNiuniuStart", {
        playerUids: playingPlayers.map(function (player) {
            return player.getUid();
        }),
        balances: playingPlayers.map(function (player) {
            return player.getBalance()
        })
    });
    for (let i = 0; i < playingPlayers.length; i++) {
        let player = playingPlayers[i];
        this.broadCast("onPlayerBalanceChange", {
            uid: player.getUid(),
            balance: player.getBalance()
        });
    }
    let params = {uids: [], amounts:[], params:[]};
    for (let i = 0; i < playingPlayers.length; i++) {
        let player = playingPlayers[i];
        params.uids.push(player.getUid());
        params.amounts.push(-fee);
        params.params.push("niuniu_start");
    }
    pomelo.app.rpc.account.tableRemote.changeDiamond(null, params, function (){});
    let lastGameDealer = this.getPlayingPlayerByUid(this.dealerUid);
    this.dealerUid = 0;
    if (this.dealerType === 1) {
        //牛牛上庄
        this.step = 2;
        this.stepTS = DateUtil.nowMS();
        //上一局有牛牛的、牌最大、还在桌子上的玩家
        let dealer = lastGameDealer ? lastGameDealer : this.getFirstPlayingPlayer();
        for (let index = 0; index < this.lastGameDealerCandiateIds.length; index++) {
            let tempPlayer = this.getPlayingPlayerByUid(this.lastGameDealerCandiateIds[index]);
            if (!!tempPlayer) {
                dealer = tempPlayer;
                break;
            }
        }
        this.dealerUid = dealer.getUid();
        dealer.bet(1);
        this.broadCast("onNiuniuChooseDealer", {
            uid: this.dealerUid
        });
        this.broadCast("onNiuniuBetStart", {
            step: this.step,
            countDown : this.calculateCountDown()
        });
    } else if (this.dealerType === 2) {
        //抢庄
        this.step = 1;
        this.stepTS = DateUtil.nowMS();
        let allPlayers = this.getAllPlayers();
        allPlayers.forEach(function (player) {
            player.pushCards(4, playingPlayers[0].getUid());
        });
        this.broadCast("onNiuniuAuctionDealerStart", {
            step: this.step,
            countDown : this.calculateCountDown()
        });
    } else if (this.dealerType === 5) {
        //通比
        this.step = 2;
        this.stepTS = DateUtil.nowMS();
        this.dealerUid = 0;
        this.broadCast("onNiuniuChooseDealer", {
            uid: this.dealerUid
        });
        this.broadCast("onNiuniuBetStart", {
            step: this.step,
            countDown : this.calculateCountDown()
        });
    }
};

NiuNiuYuanBaoTable.prototype.getDealCardLength = function() {
    if (this.dealerType === 2 && this.step < 3) {
        return 4;
    } else {
        return 5;
    }
};

NiuNiuYuanBaoTable.prototype.calculateCountDown = function () {
    if (this.step <= 0 || this.step >= 4) {
        return 0;
    }
    if (this.stepTS === 0) {
        return 0;
    }
    let interval = this.actionInterval - DateUtil.getIntervalSec(this.stepTS);
    // let interval = ACTION_INTERVAL - DateUtil.getIntervalSec(this.stepTS);
    return interval <= 0 ? 0 : interval;
};

NiuNiuYuanBaoTable.prototype.finish = function () {
    let playingPlayers = this.getPlayingPlayers();
    let niuResult = [];
    for (let i = 0; i < playingPlayers.length; i++) {
        niuResult[i] = Poker.calculateNiuResult(playingPlayers[i].getCards());
    }
    let targetUid = this.dealerUid;
    //通比要找出牌力最大的哪位
    if (this.dealerType === 5) {
        targetUid = playingPlayers[0].getUid();
        let maxPower = niuResult[0].power;
        for (let i = 1; i < playingPlayers.length; i++) {
            if (niuResult[i].power > maxPower) {
                maxPower = niuResult[i].power;
                targetUid = playingPlayers[i].getUid();
            }
        }
    }

    // 牛牛上庄要记录有牛的玩家id并按牌力排降序
    if (this.dealerType === 1) {
        let resultInfo = [];
        for (let i = 0; i < playingPlayers.length; i++) {
            resultInfo[i] = {uid : playingPlayers[i].getUid(), result : niuResult[i]};
        }
        this.lastGameDealerCandiateIds = resultInfo.filter(function (item){
            return item.result.type >= Poker.HAND_TYPE.NIU_NIU;
        }).sort(function (a, b) {
            return b.result.power - a.result.power;
        }).map(function (item) {
            return item.uid;
        });
    }

    let targetNiuResult = {};
    let targetPlayerIndex = 0;
    for (let i = 0; i < niuResult.length; i++) {
        if (playingPlayers[i].getUid() === targetUid) {
            targetNiuResult = niuResult[i];
            targetPlayerIndex = i;
            break;
        }
    }

    let scores = [];
    let targetScore = 0;
    for (let i = 0; i < niuResult.length; i++) {
        let currentPlayer = playingPlayers[i];
        let score = 0;
        let multiplierForTarget = 0;
        if(this.dealerType === 2) {
            multiplierForTarget = playingPlayers[targetPlayerIndex].getMultiplierForDealer();
        } else {
            multiplierForTarget = playingPlayers[targetPlayerIndex].getMultiplierForBet();
        }
        let multiplierForBet = currentPlayer.getMultiplierForBet();
        if (multiplierForTarget === 0) {
            multiplierForTarget = 1;
        }
        if (niuResult[i].power > targetNiuResult.power) {
            score = NiuTypeMultipliers[this.multiplierType][niuResult[i].type] *
                multiplierForBet *
                multiplierForTarget *
                this.multiplier *
                this.maxAuctionDealerMultiplier;
        } else if (niuResult[i].power < targetNiuResult.power) {
            score = - NiuTypeMultipliers[this.multiplierType][niuResult[targetPlayerIndex].type] *
                multiplierForBet *
                multiplierForTarget *
                this.multiplier *
                this.maxAuctionDealerMultiplier;
        }
        scores.push(score);
        targetScore = targetScore - score;
    }
    scores[targetPlayerIndex] = targetScore;

    logger.info(`niuniu scores 0:` + JSON.stringify(scores));
    // 重新计算score，防止以小博大的情况，正负分组，筹码&分数求最大盈亏上限，按比例输赢
    let winnerScore = 0;
    let losersScore = 0;
    scores.forEach(function (score, index) {
        let player = playingPlayers[index];
        if (score > 0) {
            winnerScore += Math.min(score, player.getBalance());
        } else {
            losersScore += Math.min(-score, player.getBalance());
        }
    });

    if (winnerScore > losersScore) {
        // 输方总筹码不够，赢方按比例（最大可盈利能力）分输方总筹码
        for (let index = 0; index < scores.length; index++) {
            let score = scores[index];
            let player = playingPlayers[index];
            if (score > 0) {
                let maxScore = Math.min(score, player.getBalance());
                scores[index] = Math.floor(maxScore * losersScore / winnerScore);
            } else {
                let maxScore = Math.max(score, -player.getBalance());
                scores[index] = maxScore;
            }
        }
    } else {
        // 输的一方筹码多于赢得一方的筹码，输一方按比例输
        for (let index = 0; index < scores.length; index++) {
            let score = scores[index];
            let player = playingPlayers[index];
            if (score > 0) {
                let maxScore = Math.min(score, player.getBalance());
                scores[index] = maxScore;
            } else {
                let maxScore = Math.max(score, -player.getBalance());
                scores[index] = Math.floor(maxScore * winnerScore / losersScore);
            }
        }
    }

    logger.info(`niuniu scores:` + JSON.stringify(scores));
    let params = {uids: [], amounts:[], params:[]};
    for (let i = 0; i < niuResult.length; i++) {
        let player = playingPlayers[i];
        params.uids.push(player.getUid());
        params.amounts.push(scores[i]);
        logger.info(`niuniu scores amount:` + JSON.stringify(scores[i]));
        params.params.push("niuniu_finish");
    }
    pomelo.app.rpc.account.tableRemote.changeDiamond(null, params, function (){});

    let results = [];
    for (let i = 0; i < niuResult.length; i++) {
        let player = playingPlayers[i];
        player.finish(scores[i]);
        results.push({
            uid: player.getUid(),
            score: scores[i],
            niuniuType: niuResult[i].type,
            cards: niuResult[i].finalCards,
            balance: player.getBalance()
        });
    }

    this.broadCast("onNiuniuFinish", {
        results: results,
        targetUid: targetUid
    });

    for (let i = 0; i < niuResult.length; i++) {
        let player = playingPlayers[i];
        this.broadCast("onPlayerBalanceChange", {
            uid: player.getUid(),
            balance: player.getBalance()
        });
    }

    let gameRecord = this.getRecordJSON(playingPlayers, scores);
    pomelo.app.rpc.account.gameRecordRemote.addGameRecord(null, gameRecord, function(response) {
        if (response.resultCode != 0) {
            logger.info(`niuniuYuanBaoTable addGameRecord failed`);
            return;
        }
        logger.info(`niuniuYuanBaoTable addGameRecord ${JSON.stringify(response.gameRecord)}`);
    });

    //add gameResult
    let gameResult = this.getResultJSON(results);
    pomelo.app.rpc.account.gameResultRemote.addNiuniuGameResult(null, gameResult, function (response) {
        if (response.resultCode != 0) {
            logger.info(`niuniuYuanBaoTable addGameResult failed`);
            return;
        }
        logger.info(`niuniuYuanBaoTable addGameResult ${JSON.stringify(response.gameResult)}`);
    });

    //reset table
    this.playingFlag = 0;
    this.step = 0;
    this.stepTS = 0;
    let self = this;
    this.players.forEach(function (player) {
        if (player != null) {
            player.resetInTable();
            player.clearReady();
            player.startGetReadyCountDown(self.actionInterval + 1);
        }
    });
};

NiuNiuYuanBaoTable.prototype.beforeStart = function () {
    BaseTable.prototype.beforeStart.call(this);
    // 计算action interval
    this.actionInterval = Math.ceil(this.getNumOfPlayers() * 2.5);
    this.actionInterval = this.actionInterval > 16 ? this.actionInterval : 16; // 多人牛牛延长时间
};

NiuNiuYuanBaoTable.prototype.getStep = function () {
    return this.step;
};

NiuNiuYuanBaoTable.prototype.getDealerType = function () {
    return this.dealerType;
};

NiuNiuYuanBaoTable.prototype.getDealerUid = function () {
    return this.dealerUid;
};

NiuNiuYuanBaoTable.prototype.betForDealer = function (player, multiplier) {
    player.betForDealer(multiplier);
    this.broadCast("onNiuniuPlayerBetForDealer", {
        uid: player.getUid(),
        multiplier: multiplier
    });
    this.checkNextRound();
};

NiuNiuYuanBaoTable.prototype.bet = function (player, multiplier) {
    player.bet(multiplier);
    this.broadCast("onNiuniuPlayerBet", {
        uid: player.getUid(),
        multiplier: multiplier
    });
    this.checkNextRound();
};

NiuNiuYuanBaoTable.prototype.commit = function (player) {
    player.commit();
    this.broadCast("onNiuniuPlayerCommit", {
        uid: player.getUid()
    });
    this.checkNextRound();
};

NiuNiuYuanBaoTable.prototype.checkNextRound = function () {
    let self = this;
    let playingPlayers = this.getPlayingPlayers();
    let isAllActioned = true;
    playingPlayers.forEach(function (player) {
        if (player.getAction() <= 0) {
            isAllActioned = false;
        }
    });
    if (!isAllActioned) {
        return;
    }
    playingPlayers.forEach(function (player) {
        player.clearAction();
    });
    if (this.step === 1) {
        this.step = 2;
        this.stepTS = DateUtil.nowMS();
        let maxMultiplierForDealer = 0;
        playingPlayers.forEach(function (player) {
            if (player.getMultiplierForDealer() > maxMultiplierForDealer) {
                maxMultiplierForDealer = player.getMultiplierForDealer();
            }
        });
        let dealerCandidates = [];
        playingPlayers.forEach(function (player) {
            if (player.getMultiplierForDealer() === maxMultiplierForDealer) {
                dealerCandidates.push(player);
            }
        });
        let dealer = Random.choice(dealerCandidates);
        this.dealerUid = dealer.getUid();
        this.broadCast("onNiuniuChooseDealer", {
            uid: this.dealerUid
        });
        dealer.setAction();
        this.broadCast("onNiuniuBetStart", {
            step: this.step,
            countDown: this.calculateCountDown()
        });
    } else if (this.step === 2) {
        this.step = 3;
        this.stepTS = DateUtil.nowMS();
        let allPlayers = this.getAllPlayers();
        if (this.dealerType === 2) {
            allPlayers.forEach(function (player) {
                player.pushCards(1, self.dealerUid);
            });
        } else {
            allPlayers.forEach(function (player) {
                player.pushCards(5, playingPlayers[0].getUid());
            });
        }
        this.broadCast("onNiuniuCommitStart", {
            step: this.step,
            countDown: this.calculateCountDown()
        });
    } else if (this.step === 3) {
        this.finish();
    }
};

NiuNiuYuanBaoTable.prototype.toJSON = function () {
    let numOfCards = 0;
    if (this.step === 1) {
        numOfCards = 4;
    } else if (this.step === 2)  {
        //抢庄的时候有4张牌
        if (this.dealerType === 2) {
            numOfCards = 4;
        //通比和牛牛上装0张牌
        } else {
            numOfCards = 0;
        }
    } else if (this.step === 3) {
        numOfCards = 5;
    } else {
        numOfCards = 0;
    }
    let playersJSON = [];
    this.players.forEach(function (player) {
        if (player != null) {
            let playerJSON = player.toJSON();
            if (player.isReady()) {
                playerJSON['numOfCards'] = numOfCards;
            } else {
                playerJSON['numOfCards'] = 0;
            }
            playersJSON.push(playerJSON);
        }
    });
    return {
        tid: this.tid,
        code: this.code,
        ownerUid: this.creatorUid,
        dealerUid: this.dealerUid,
        multiplier: this.multiplier,
        step: this.step,
        stepCountDown: this.calculateCountDown(),
        playingFlag: this.playingFlag,
        maxPlayers: this.maxPlayers,
        dealerType: this.dealerType,
        multiplierType: this.multiplierType,
        maxAuctionDealerMultiplier: this.maxAuctionDealerMultiplier,
        players : playersJSON
    };
};

NiuNiuYuanBaoTable.prototype.getResultJSON = function (results) {
    return {
        gid : this.gid,
        tid : this.tid,
        code : this.code,
        multiplier : this.multiplier,
        startTime : this.gameStartTime,
        endTime : DateUtil.nowMS(),
        dealerType : this.dealerType,
        multiplierType : this.NiuTypeMultiplierToString(this.multiplierType),
        maxAuctionDealerMultiplier : this.maxAuctionDealerMultiplier,
        results : results
    }
};

NiuNiuYuanBaoTable.prototype.getRecordJSON = function (playingPlayers, scores) {
    let playerScores = [];
    for(let [index, playingPlayer] of playingPlayers.entries()) {
        playerScores.push({
            uid : playingPlayer.getUid(),
            nickname: playingPlayer.getNickname(),
            score : scores[index]
        });
    }
    return {
        gameId: this.gid,
        tableId: this.tid,
        time: DateUtil.nowMS(),
        playerScores: playerScores
    }
};

NiuNiuYuanBaoTable.prototype.tick = function () {
    BaseTable.prototype.tick.call(this);
    if (this.playingFlag <= 0) {
        return;
    }
    if (this.step < 1 || this.step >= 4) {
        return;
    }
    if (this.calculateCountDown() > 0) {
        return;
    }
    let self = this;
    let playingPlayers = this.getPlayingPlayers();
    if (this.step === 1) {
        playingPlayers.forEach(function (player) {
            if (player.getAction() < 1) {
                player.betForDealer(0);
                self.broadCast("onNiuniuPlayerBetForDealer", {
                    uid: player.getUid(),
                    multiplier: 0
                });
            }
        });
        this.checkNextRound();
    } else if (this.step === 2) {
        playingPlayers.forEach(function (player) {
            if (player.getAction() < 1) {
                player.bet(1);
                self.broadCast("onNiuniuPlayerBet", {
                    uid: player.getUid(),
                    multiplier: 1
                });
            }
        });
        this.checkNextRound();
    } else if (this.step === 3) {
        playingPlayers.forEach(function (player) {
            if (player.getAction() < 1) {
                player.commit();
                self.broadCast("onNiuniuPlayerCommit", {
                    uid: player.getUid()
                });
            }
        });
        this.checkNextRound();
    }
};

NiuNiuYuanBaoTable.prototype.onAddPlayer = function (player) {
    player.push("onInitializeNiuniuTable",
        {table: this.toJSON(), myCards: player.getDisplayCards(this)}
    );
    this.broadCastWithout("onAddOrUpdateNiuniuPlayer",
        {player : player.toJSON()}, player.getUid());
};

NiuNiuYuanBaoTable.prototype.toYuanBaoTable= function () {
    let description = "";
    
    if(this.dealerType === 2){
        description = "" + dealerDes[this.dealerType - 1] + "   " + this.maxAuctionDealerMultiplier + "倍" + "   " + multiplierTypes[this.multiplierType - 1];
    }else{
        description = "" + dealerDes[this.dealerType - 1] + "   " + multiplierTypes[this.multiplierType - 1];
    }

    return {
        "tid": this.tid,
        "code": this.code,
        "minStack": this.room.minStack,
        "numOfPlayers": this.getNumOfPlayers(),
        "maxPlayers": this.maxPlayers,
        "description": description,
        "multiplier": this.multiplier,
        "createTime": this.createTime,
    };
};

NiuNiuYuanBaoTable.prototype.NiuTypeMultiplierToString = function (multiplierType) {
    let NiuTypeMultiplier = NiuTypeMultipliers[multiplierType] || [];
    return JSON.stringify(NiuTypeMultiplier);
};

module.exports.create = function (tableCfg) {
    let table = new NiuNiuYuanBaoTable();
    table.tid = tableCfg['tid'];
    table.code = tableCfg['code'];
    table.room = tableCfg['room'];
    table.multiplier = table.room.multiplier;
    table.maxPlayers = tableCfg['maxPlayers'];
    table.dealerType = tableCfg['dealerType'];
    table.multiplierType = tableCfg['multiplierType'];
    table.maxAuctionDealerMultiplier = tableCfg['maxAuctionDealerMultiplier'];
    table.visible = tableCfg['visible'];
    table.creatorUid = tableCfg['creatorUid'];
    table.gid = tableCfg['gid'];
    table.createTime = DateUtil.nowMS();
    for (let i = 0; i < table.maxPlayers; i++) {
        table.players.push(null);
    }
    return table;
};

